import types


# class Wrapper:
#     def __init__(self, obj):
#         self.obj = obj
#
#     def __getattr__(self, attr):
#         ret = None  # 可以找不到返回 None，也可以抛出异常。
#         if hasattr(self.obj, attr):
#             ret = getattr(self.obj, attr)
#         return ret
#
#
# class Sample:
#     def __init__(self, x, y):
#         self.x = x
#         self.y = y
#
#     def add(self):
#         return self.x + self.y
#
#     def minus(self):
#         return self.x - self.y
#
#
# w = Wrapper(Sample(100, 200))
# print(w.x)
# print(w.y)
# print(w.add())
# print(w.minus())

'''
输出如下：
100
200
300
-100
'''


class LogProxy:
    def __init__(self, obj, method):
        self.obj = obj
        self.method = method

    def __call__(self, *args, **kws):
        print('Log: ' + str(self.obj) + ' call ' + self.method.__name__)
        ret = self.method(*args, **kws)
        print('Log: ' + str(self.obj) + ' call ' + self.method.__name__ + ' finished')
        return ret


class Proxy:
    def __init__(self, clz, pclz):
        self.clz = clz
        self.pclz = pclz
        self.proxies = {}  # 查表提高效率

    def __call__(self, *args, **kws):
        self.obj = self.clz(*args, **kws)  # 调用被代理类的构造函数创建实例
        return self

    def __getattr__(self, attr):  # 调用方法之前需要通过 getattr 查找方法
        ret = None
        if hasattr(self.obj, attr):  # 查看被代理实例成员是否存在
            ret = getattr(self.obj, attr)
            if isinstance(ret, types.MethodType):  # 如果该成员是方法
                if ret not in self.proxies:  # 如果该方法的代理没有被生成
                    self.proxies[ret] = self.pclz(self.obj, ret)  # 创建该方法的代理
                return self.proxies[ret]
        return ret


class ProxyFactory:
    def __init__(self, pclz):
        self.pclz = pclz

    def __call__(self, clz):
        return Proxy(clz, self.pclz)


if __name__ == '__main__':
    @ProxyFactory(LogProxy)
    class Sample:
        def __init__(self, name):
            self.name = name

        def print_name(self):
            print(self.name)
    s = Sample('xxx')
    s.print_name()

'''
输出如下：
Log: <__main__.Sample object at 0x7f8edd6020b8> call print_name
xxx
Log: <__main__.Sample object at 0x7f8edd6020b8> call print_name finished
s.name: xxx
'''
